package leo.peersim.abcast;

import java.util.HashMap;
import java.util.Vector;

import leo.peersim.common.FSMOperationRequest;
import leo.peersim.common.LamportEvent;

import org.apache.log4j.Logger;

import peersim.core.CommonState;
import peersim.core.Node;

/**
 * 
 * @author leo
 */
public class ABCastRound extends LamportEvent {
	
	private Logger logger = Logger.getLogger(this.getClass().getName());
	
	private String roundID;
	
	private Node requestorNode;
	private long requestorNodeTimestamp;
	
	private Node executingNode;
	
	private long atomicTimestamp;
	
	private FSMOperationRequest clientRequest;
	
	private boolean completed;
	
	private HashMap<Node, Long> proposedTimestamps;
	
	private long debug_acceptedTime =-1;
	private long debug_agreedTime = -1;

	/**
	 * 
	 * @param transactionID
	 * @param requestorNode
	 * @param requestorNodeTimestamp
	 * @param executingNode
	 * @param atomicTimestamp
	 * @param clientRequest
	 */
	public ABCastRound(String transactionID, Node requestorNode, long requestorNodeTimestamp, Node executingNode, long atomicTimestamp, FSMOperationRequest clientRequest) {
		this.roundID = transactionID;
		
		this.requestorNode = requestorNode;
		this.requestorNodeTimestamp = requestorNodeTimestamp;
		
		this.executingNode = executingNode;
		
		this.atomicTimestamp = atomicTimestamp;
		
		this.clientRequest = clientRequest;
		
		this.completed = false;
		
		this.proposedTimestamps = new HashMap<Node, Long>(); //FIXME
		/* consider the choice of modeling different kinds of transactions depending
		 * on agent type //this is the python approach!!
		 */
	}
	
	/**
	 * 
	 * @param proposer
	 * @param proposal
	 * @return true if the proposal} made by proposer did not be already received
	 */
	public boolean addProposal(Node proposer, long proposal) {
		
		if (this.proposedTimestamps.isEmpty()) {
			this.debug_acceptedTime = CommonState.getTime();
		}
		
		if (this.proposedTimestamps.containsKey(proposer)) {
			return false;
		} else {
			this.proposedTimestamps.put(proposer, new Long(proposal));
			return true;
		}
	}
	
	public HashMap<Node,Long> getProposals() {
		return this.proposedTimestamps;
	}
	
	public boolean agree(long agreedTimestamp) {
		if (this.completed) {
			logger.error("Transaction TID:" + this.getID() + " is already completed! Code has errors!");
			return false;
			//TODO
		}
		this.completed = true;
		this.atomicTimestamp = agreedTimestamp;
		this.debug_agreedTime = CommonState.getTime();
		return true;
	}
	
	public FSMOperationRequest getFSMCommand() {
		return this.clientRequest;
	}
	
	public boolean hasBeenCompleted() {
		return this.completed;
	}
	
	public String getID() {
		return this.roundID;
	}

	@Override
	public long getLamportNodeID() {
		//return this.executingNode.getID();  //!WRONG! this does not ensure total order!
		return this.requestorNode.getID();
	}

	@Override
	public long getLamportTimestamp() {
		return this.atomicTimestamp;
	}
	
	public Node getRequestorNode() {
		return this.requestorNode;
	}

	public long getDebug_acceptedTime() {
		return this.debug_acceptedTime;
	}
	
	public long getDebug_agreedTime() {
		return this.debug_agreedTime;
	}
	
}


